﻿using Microsoft.Extensions.Options;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Receiving;
using MQTTnet.Protocol;
using MQTTnet.Server;
using MQTTnet.Server.Internal;
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace IntelligentEscalator.BUS.Mqtt
{
    public class FXMqttServer
    {
        public IMqttServer mqttServer = null;
        public string username = string.Empty;
        public string password = string.Empty;
        public int port = 0;

        public FXMqttServer()
        {
            try
            {
                username = AppSettingsHelper.Configuration["MqttServer:Username"];
                password = AppSettingsHelper.Configuration["MqttServer:Password"];
                port = Convert.ToInt32(AppSettingsHelper.Configuration["MqttServer:Port"]);
                MqttServer();
            }
            catch (Exception ex)
            {
                LogHelper.MqttLog("Mqtt:" + ex.ToString());
            }
        }

        public async void MqttServer()
        {
            // 1. 创建 MQTT 连接验证，用于连接鉴权
            MqttServerConnectionValidatorDelegate connectionValidatorDelegate = new MqttServerConnectionValidatorDelegate(
                p =>
                {
                    // p 表示正在发起的一个链接的上下文

                    // 用户名和密码验证
                    // 大部分情况下，我们应该使用客户端加密 token 验证，也就是可客户端 ID 对应的密钥加密后的 token
                    if (p.Username != username && p.Password != password)
                    {
                        // 验证失败，告诉客户端，鉴权失败
                        p.ReasonCode = MQTTnet.Protocol.MqttConnectReasonCode.BadUserNameOrPassword;
                    }
                }
            );


            // 2. 创建 MqttServerOptions 的实例，用来定制 MQTT 的各种参数
            MqttServerOptions options = new MqttServerOptions();

            // 3. 设置各种选项
            // 客户端鉴权
            options.ConnectionValidator = connectionValidatorDelegate;

            // 设置服务器端端口号
            options.DefaultEndpointOptions.Port = port;

            // 4. 创建 MqttServer 实例
            mqttServer = new MqttFactory().CreateMqttServer();

            // 5. 设置 MqttServer 的属性
            // 设置消息订阅通知
            mqttServer.ClientSubscribedTopicHandler = new MqttServerClientSubscribedTopicHandlerDelegate(SubScribedTopic);
            // 设置消息退订通知
            mqttServer.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(UnScribedTopic);
            // 设置消息处理程序
            mqttServer.UseApplicationMessageReceivedHandler(MessageReceived);
            // 设置客户端连接成功后的处理程序
            mqttServer.UseClientConnectedHandler(ClientConnected);
            // 设置客户端断开后的处理程序
            mqttServer.UseClientDisconnectedHandler(ClientDisConnected);

            // 启动服务器
            await mqttServer.StartAsync(options);
            LogHelper.MqttLog("服务器启动成功");
        }

        // 客户端发起订阅主题通知
        private static void SubScribedTopic(MqttServerClientSubscribedTopicEventArgs args)
        {
            // 获取客户端识别码
            string clientId = args.ClientId;
            // 获取客户端发起的订阅主题
            string topic = args.TopicFilter.Topic;

            LogHelper.MqttLog($"客户端[{clientId}]已订阅主题:{topic}");
        }

        // 客户端取消主题订阅通知
        private static void UnScribedTopic(MqttServerClientUnsubscribedTopicEventArgs args)
        {
            // 获取客户端识别码
            string clientId = args.ClientId;
            // 获取客户端发起的订阅主题
            string topic = args.TopicFilter;

            LogHelper.MqttLog($"客户端[{clientId}]已退订主题:{topic}");
        }

        // 接收客户端发送的消息
        private static void MessageReceived(MqttApplicationMessageReceivedEventArgs args)
        {
            // 获取消息的客户端识别码
            string clientId = args.ClientId;
            // 获取消息的主题
            string topic = args.ApplicationMessage.Topic;
            // 获取发送的消息内容
            string payload = Encoding.UTF8.GetString(args.ApplicationMessage.Payload);
            // 获取消息的发送级别(Qos)
            var qos = args.ApplicationMessage.QualityOfServiceLevel;
            // 获取消息的保持形式
            bool retain = args.ApplicationMessage.Retain;

            //LogHelper.MqttLog($"客户端[{clientId}] >> 主题: [{topic}] 内容: [{payload}] Qos: [{qos}] Retain:[{retain}]");

        }

        // 客户端连接成功后的的处理通知
        private static void ClientConnected(MqttServerClientConnectedEventArgs args)
        {
            // 获取客户端识别码
            string clientId = args.ClientId;

            LogHelper.MqttLog($"新客户端[{clientId}] 加入");
        }

        // 客户端断开连接通知
        private static void ClientDisConnected(MqttServerClientDisconnectedEventArgs args)
        {
            // 获取客户端识别码
            string clientId = args.ClientId;

            LogHelper.MqttLog($"新客户端[{clientId}] 已经离开");
        }
    }
}
